
/****************************************************************************/
/*Copyright (c) 2011, Florent DEVILLE.                                      */
/*All rights reserved.                                                      */
/*                                                                          */
/*Redistribution and use in source and binary forms, with or without        */
/*modification, are permitted provided that the following conditions        */
/*are met:                                                                  */
/*                                                                          */
/* - Redistributions of source code must retain the above copyright         */
/*notice, this list of conditions and the following disclaimer.             */
/* - Redistributions in binary form must reproduce the above                */
/*copyright notice, this list of conditions and the following               */
/*disclaimer in the documentation and/or other materials provided           */
/*with the distribution.                                                    */
/* - The names of its contributors cannot be used to endorse or promote     */
/*products derived from this software without specific prior written        */
/*permission.                                                               */
/* - The source code cannot be used for commercial purposes without         */ 
/*its contributors' permission.                                             */
/*                                                                          */
/*THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS       */
/*"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT         */
/*LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS         */
/*FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE            */
/*COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,       */
/*INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,      */
/*BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;          */
/*LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER          */
/*CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT        */
/*LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN         */
/*ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE           */
/*POSSIBILITY OF SUCH DAMAGE.                                               */
/****************************************************************************/

#include "GRTViewport.h"

#include <QTimer>
#include <QKeyEvent>
#include <QWheelEvent>
#include <QMouseEvent>
#include <QMessageBox>
//#include <QMenuBar>
#include "GRTContextualMenu.h"

#include <math.h>

#include "RTMatrix44.h"

#include "GRTGLVisu.h"

//handle the scene
#include "GRTScene.h"

#include "GRTGeoSphere.h"
#include "GRTGeoBox.h"
#include "GRTGeoCylinder.h"
#include "GRTGeoCone.h"
#include "GRTGeoTorus.h"
#include "GRTGeoMesh.h"

#include "GRTDirectionalLight.h"
#include "GRTDirectionalAt.h"
#include "GRTPointLight.h"
#include "GRTSpotLight.h"
#include "GRTSpotAt.h"

#include "GRTCameraEye.h"
#include "GRTMemoryLeak.h"
//constructor with shared widget (opengl context)
GRTViewport::GRTViewport(QWidget* parent, QGLWidget* shareWidget) : QGLWidget(parent, shareWidget), _eye(0, 2, 5), _at(0, 0, 0), 
	_up(0, 1, 0)
{
	int fps = 60;
	int seconde = 1000; // 1 seconde = 1000 ms
    int timerInterval = seconde / fps;
    _timer = new QTimer(this);
    _timer->start( timerInterval );
	connect(_timer, SIGNAL(timeout()), this, SLOT(timeOutSlot()));

	connect(this, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(menuContextuel(QPoint)));

	_mousePreviousPosition = RTVector3f(-1, -1, -1);
	_mouseClickPosition = RTVector3f(-1, -1, -1);

	//set the original orientation camera
	RTVector3f direction = _at - _eye;
	direction.normalize();
	float cosAngleX = direction.dot(RTVector3f(0, 0, -1));
	float angleX = acosf(cosAngleX);
	_orientationCamera = RTVector3f(-angleX, 0, 0);
	_bRotationCamera = false;
	SCENE.setGRTCameraEye(_eye);

	//create the right click menu
	_rightClickMenu = new GRTContextualMenu(this);
	connect(_rightClickMenu->_actionViewFront, SIGNAL(triggered()), this, SLOT(setCameraFrontView()));
	connect(_rightClickMenu->_actionViewBack, SIGNAL(triggered()), this, SLOT(setCameraBackView()));
	connect(_rightClickMenu->_actionViewLeft, SIGNAL(triggered()), this, SLOT(setCameraLeftView()));
	connect(_rightClickMenu->_actionViewRight, SIGNAL(triggered()), this, SLOT(setCameraRightView()));
	connect(_rightClickMenu->_actionToggleWireframe, SIGNAL(triggered()), this, SLOT(toggleWireframe()));
	connect(_rightClickMenu->_actionViewCamera, SIGNAL(triggered()), this, SLOT(setCameraGRTSceneView()));
	
	//to get the events
	setFocusPolicy(Qt::StrongFocus);

	//to enable the right click
	setContextMenuPolicy(Qt::CustomContextMenu);

	//enable the mouse move event
	setMouseTracking(true);

	//set the wireframe to false by default
	_wireframe = false;

	//set the translation mode to false
	_bTranslation = false;
	_bRotation = false;
	_bScale = false;
	_bAdd = false;
	_typeToAdd = GT_NONE;
	_lightToAdd = LT_NONE;

	//_bDrawTranslationMark = false;
	_displacementMode = D_NONE;

	_cameraGRTScene = false;
}

GRTViewport::~GRTViewport(void)
{
}

void GRTViewport::initializeGL(void)
{
    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glClearDepth(1.0f);
    glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LEQUAL);
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

	//enable light
	glEnable(GL_LIGHTING);

	//use gouraud
	glShadeModel(GL_SMOOTH);

	//only front side lighted
	glLightModelf(GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE);

	//round points
	glEnable(GL_POINT_SMOOTH);
}

void GRTViewport::resizeGL(int width, int height)
{
	if(height == 0)
        height = 1;
    glViewport(0, 0, width, height);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(45.0f, (GLfloat)width/(GLfloat)height, 0.1f, 100.0f);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

	
	glGetDoublev( GL_PROJECTION_MATRIX, _projection );
	glGetIntegerv( GL_VIEWPORT, _viewport );
}

void GRTViewport::paintGL(void)
{
	//set the wireframe mode
	applyWireframe();

	//clear buffers
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	//clear color buffer
	glClearColor(0.3f, 0.3f, 0.3f, 1.f);

	//set modelview matrix to identity
	glMatrixMode (GL_MODELVIEW);
    glLoadIdentity();

	//set camera
	gluLookAt(_eye.x, _eye.y, _eye.z, 
		_at.x, _at.y, _at.z, 
		_up.x, _up.y, _up.z);

	//save model view matrix
	glGetDoublev( GL_MODELVIEW_MATRIX, _modelview );

	//draw the entire scene
	if(_cameraGRTScene)
		SCENE.drawGRTSceneAsRender();
	else
	{
		SCENE.drawGRTScene();
		//draw the grid
		VISU.drawGrid(_eye);
	}

	//if an object is selected, draw  the marks
	if(SCENE.getSelectedObject() != 0 && !_cameraGRTScene)
	{
		switch(_displacementMode)
		{
			case D_TRANSLATE:
				SCENE.getSelectedObject()->drawTranslationMark();
				break;

			case D_ROTATE:
				SCENE.getSelectedObject()->drawRotationMark();
				break;

			case D_SCALE:
				SCENE.getSelectedObject()->drawScaleMark();
				break;
			default:
				break;
		}
	}

	//disable all the lights
	VISU.disableUnusedLights(SCENE.getLightID());

	//flush the scene buffer
	glFlush();
  
}

void GRTViewport::timeOutSlot()
{
	updateGL();
}

void GRTViewport::keyPressEvent( QKeyEvent* keyEvent)
{
	//calculate front vectors
	RTVector3f front;
	/*front = _at - _eye;
	front.normalize();*/

	//calculate right vectors
	RTVector3f right;
	/*right = _up.cross(front);
	right.normalize();*/
	
	RTVector3f up;

	float speed = 0.5;

	//offset right vector
	RTVector3f moveRight;
	/*moveRight = right * speed;*/

	//offset up vector
	RTVector3f moveUp;
	/*moveUp = _up * speed;*/

	switch(keyEvent->key())
	{
		case Qt::Key_Right:
			getCameraMark(right, up, front);
			moveRight = right * speed;
			_eye = _eye - moveRight;
			_at = _at - moveRight;
			SCENE.setGRTCameraEye(_eye);
			break;
		case Qt::Key_Left:
			getCameraMark(right, up, front);
			moveRight = right * speed;
			_eye = _eye + moveRight;
			_at = _at + moveRight;
			SCENE.setGRTCameraEye(_eye);
			break;
		case Qt::Key_Up:
			getCameraMark(right, up, front);
			moveUp = _up * speed;
			_eye = _eye + moveUp;
			_at = _at + moveUp;
			SCENE.setGRTCameraEye(_eye);
			break;
		case Qt::Key_Down:
			getCameraMark(right, up, front);
			moveUp = _up * speed;
			_eye = _eye - moveUp;
			_at = _at - moveUp;
			SCENE.setGRTCameraEye(_eye);
			break;
		
		case Qt::Key_Delete:
			if(SCENE.deleteSelectedObject())
			{
				emit objectUnselected(0);
			}
			break;

		default:
			keyEvent->ignore();
			break;
			
	}

	//give the camera position to the scene
	/*SCENE.setGRTCameraEye(_eye);*/

	//update();
}

void GRTViewport::wheelEvent(QWheelEvent* wheelEvent)
{
	//get the distance to move
	float dist = wheelEvent->delta();
	dist /= 15;
	dist /= 8;

	//calculate the front vector
	RTVector3f front;
	front = _at - _eye;
	front.normalize();

	//calculate the translation vector
	front = front * dist;

	//translate
	_eye = _eye + front;
	_at = _at + front;

	//give the camera position to the scene
	SCENE.setGRTCameraEye(_eye);

	//update screen
	//update();
}

void GRTViewport::mouseMoveEvent(QMouseEvent* mouseEvent)
{
	//if camera mode is scene, quit
	if (_cameraGRTScene)
		return;

	//if rotation mode
	if(_bRotationCamera)
	{
		rotateCamera(mouseEvent->x(), mouseEvent->y());
		return;
	}

	//if translation mode
	if(_bTranslation)
	{
		translateSelectedObject(mouseEvent->x(), mouseEvent->y());
		return;
	}

	//if rotation mode
	if(_bRotation)
	{
		rotateSelectedObject(mouseEvent->x(), mouseEvent->y());
		return;
	}

	if(_bScale)
	{
		scaleSelectedObject(mouseEvent->x(), mouseEvent->y());
		return;
	}

	//test if the mouse is over a mark
	GRTMovable* selectedObject = SCENE.getSelectedObject();
	if(selectedObject != 0)
	{
		if(_displacementMode != D_NONE)
		{
			float wx = 0;
			float wy = 0;
			float wz = 0;
			updateGL();
			screenToWorld(mouseEvent->x(), mouseEvent->y(), wx, wy, wz);

			switch(_displacementMode)
			{
				case D_TRANSLATE:
					selectedObject->isTranslationMark(RTVector3f(wx, wy, wz));	 
					break;
				case D_ROTATE:
					selectedObject->isRotationMark(RTVector3f(wx, wy, wz));
					break;
				case D_SCALE:
					selectedObject->isScaleMark(RTVector3f(wx, wy, wz));
					break;
				default:
					break;
			}
		}
	}
}

void GRTViewport::mousePressEvent(QMouseEvent* mouseEvent)
{
	//left button clicked
	if (mouseEvent->button() == Qt::LeftButton)
	{
		//save the mouse position
		_mousePreviousPosition.x = mouseEvent->x();
		_mousePreviousPosition.y = mouseEvent->y();
		_mouseClickPosition.x = mouseEvent->x();
		_mouseClickPosition.y = mouseEvent->y();

		//if an object is selected
		if(SCENE.getSelectedObject() != 0)
		{
			if(SCENE.getSelectedObject()->_hoover.norme2() != 0 && _displacementMode == D_TRANSLATE)
			{
				_bTranslation = true;
				return;
			}
			if(SCENE.getSelectedObject()->_hoover.norme2() != 0 && _displacementMode == D_ROTATE)
			{
				_bRotation = true;
				return;
			}
			if(SCENE.getSelectedObject()->_hoover.norme2() != 0 && _displacementMode == D_SCALE)
			{
				_bScale = true;
				return;
			}
		}

		//set camera rotation mode
		_bRotationCamera = true;
		
	}
}

void GRTViewport::mouseReleaseEvent(QMouseEvent* mouseEvent)
{
	//left button clicked
	if (mouseEvent->button() == Qt::LeftButton)
	{
		//custom click event
		if(_mouseClickPosition.x == mouseEvent->x() && _mouseClickPosition.y == mouseEvent->y())
		{
			if(_bAdd)
			{
				addPrimitive();
				addLight();
			}
			else
			{
				GRTMovable* firstSelection = 0;
				if (SCENE.getSelectedObject() != 0) firstSelection = SCENE.getSelectedObject();
				
				SCENE.unselectAll();
				selectObject(mouseEvent->x(), mouseEvent->y());

				//CTRL + Click
				if ((mouseEvent->modifiers() & Qt::ControlModifier) == Qt::ControlModifier)
				{
					if (SCENE.getSelectedObject() != 0 && SCENE.getSelectedObject() != firstSelection)
					{
						if( firstSelection != 0) firstSelection->setSelected(true);
						SCENE.setFirstSelectedObject(firstSelection);
					}
				}
				else
					SCENE.setFirstSelectedObject(0);

			}

		}

		//if end of moving
		_mousePreviousPosition.x = -1;
		_mousePreviousPosition.y = -1;

		_bRotationCamera = false;
		_bTranslation = false;
		_bRotation = false;
		_bScale = false;
	}
}

//handle double click
void GRTViewport::mouseDoubleClickEvent (QMouseEvent* /*mouseEvent*/)
{
	//select the pointed object (if any)
	//selectObject(mouseEvent->x(), mouseEvent->y());
}

//void* GRTViewport::operator new(size_t size)
//{
//	return _aligned_malloc(size, 16);
//}

//void GRTViewport::operator delete(void* p)
//{
//	_aligned_free(p);
//}

//void GRTViewport::drawGrid()
//{
//	//calculate grid size
//	float distance = abs(_eye.y);
//
//	const float treshold = 10;
//	const int minimumLength = 50;
//	float length = distance < treshold ? minimumLength : minimumLength+distance;
//	float size = (int)length;
//
//	//draw grid
//	glMatrixMode(GL_MODELVIEW);
//
//	glPushMatrix();
//	glTranslatef(floor(_eye.x), 0, floor(_eye.z));
//	glLineWidth(2);
//		for(int row = 0; row < size; row++)
//		{
//			float offset = -length/2 + row;
//			glPushMatrix();
//			glTranslatef(0, 0, offset);
//			glBegin(GL_LINES);
//				glColor3f(0, 0, 0);
//				glVertex3f(-length/2, 0, 0);
//				glVertex3f(length/2, 0, 0);
//			glEnd();
//			glPopMatrix();
//
//			glPushMatrix();
//			glTranslatef(offset, 0, 0);
//			glBegin(GL_LINES);
//				glColor3f(0, 0, 0);
//				glVertex3f(0, 0, -length/2);
//				glVertex3f(0, 0, length/2);
//			glEnd();
//			glPopMatrix();
//		}
//	glPopMatrix();
//}

void GRTViewport::menuContextuel(const QPoint& pos)
{
	_rightClickMenu->exec(this->mapToGlobal(pos));
}

//set the camera position and orientation for a front view
void GRTViewport::setCameraFrontView()
{
	//set position
	_eye.x = 0;
	_eye.y = 2;
	_eye.z = 15;

	//set at (scene center)
	_at.x = _at.y = _at.z = 0;

	//set orientation
	RTVector3f direction = _at - _eye;
	direction.normalize();

	float cosAngleX = direction.dot(RTVector3f(0, 0, -1));
	float angleX = acosf(cosAngleX);
	_orientationCamera = RTVector3f(-angleX, 0, 0);

	_cameraGRTScene = false;
}

//set the camera position and orientation for a back view
void GRTViewport::setCameraBackView()
{
	//set position
	_eye.x = 0;
	_eye.y = 2;
	_eye.z = -15;

	//set at (scene center)
	_at.x = _at.y = _at.z = 0;

	//set orientation
	RTVector3f direction = _at - _eye;
	direction.normalize();

	float cosAngleX = direction.dot(RTVector3f(0, 0, 1));
	float angleX = acosf(cosAngleX);
	_orientationCamera = RTVector3f(-angleX, 3.14f, 0);

	_cameraGRTScene = false;
}

//set the camera position and orientation for a left view
void GRTViewport::setCameraLeftView()
{
	//set position
	_eye.x = -15;
	_eye.y = 2;
	_eye.z = 0;

	//set at (scene center)
	_at.x = _at.y = _at.z = 0;

	//set orientation
	RTVector3f direction = _at - _eye;
	direction.normalize();

	float cosAngleX = direction.dot(RTVector3f(1, 0, 0));
	float angleX = acosf(cosAngleX);
	_orientationCamera = RTVector3f(-angleX, -3.14f/2.f, 0);

	_cameraGRTScene = false;
}

//set the camera position and orientation for a right view
void GRTViewport::setCameraRightView()
{
	//set position
	_eye.x = 15;
	_eye.y = 2;
	_eye.z = 0;

	//set at (scene center)
	_at.x = _at.y = _at.z = 0;

	//set orientation
	RTVector3f direction = _at - _eye;
	direction.normalize();

	float cosAngleX = direction.dot(RTVector3f(1, 0, 0));
	float angleX = acosf(cosAngleX);
	_orientationCamera = RTVector3f(-angleX, 3.14f/2.f, 0);

	_cameraGRTScene = false;
}

//set the camera to the scene camera
void GRTViewport::setCameraGRTSceneView()
{
	_cameraGRTScene = true;
	refreshCameraGRTScene();
}

void GRTViewport::toggleWireframe()
{
	if (_wireframe)
		glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
	else
		glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );

	_wireframe = !_wireframe;
}

//select an object in the scene
void GRTViewport::selectObject(int x, int y)
{
	//update the display to get the good matrices : the ones of the current viewport
	updateGL();

	//calculate the world coordinates of the clicked point
	float wx, wy, wz;

	float sx = x;
	float sy = y;
	screenToWorld(sx, sy, wx, wy, wz);

	//check if the click point is a primitive of the scene
	GRTMovable* g = SCENE.getGRTMovable(RTVector3f(wx, wy, wz));

	if (g == 0)//nothing has been clicked
	{
		SCENE.unselectObject();
		emit objectUnselected(0);
		emit objectGRTMovableUnselected(0);
	}
	else//a primitive was clicked
	{
		SCENE.setSelectedObject(g);
		switch(g->getGRTMovableType())
		{
			case MT_GEOMETRY:
				emit objectGRTGeometrySelected((GRTGeometry*)g);
				break;

			case MT_LIGHT:
				emit objectUnselected(0);
				emit objectLightSelected((GRTILight*)g);
				break;

			case MT_CAMERA:
				emit objectCameraSelected((GRTICamera*)g);
				break;

			default:
				break;
		}

		//send the selected movable signal
		emit objectGRTMovableSelected(g);
	}
	
}

//activate the translation mode
void GRTViewport::toggleTranslationMode()
{
	if(_displacementMode != D_TRANSLATE)
		_displacementMode = D_TRANSLATE;
	else
		_displacementMode = D_NONE;
	//_bDrawTranslationMark = !_bDrawTranslationMark;
}

//toggle the rotation mode
void GRTViewport::toggleRotationMode()
{
	if(_displacementMode != D_ROTATE)
		_displacementMode = D_ROTATE;
	else
		_displacementMode = D_NONE;
}

//toggle the scale mode
void GRTViewport::toggleScaleMode()
{

	if(_displacementMode != D_SCALE)
		_displacementMode = D_SCALE;
	else
		_displacementMode = D_NONE;
}

//toggle the add primitive mode
void GRTViewport::toggleAddModeSphere()
{
	if(_bAdd)
	{
		_bAdd = false;
		_typeToAdd = GT_NONE;
	}
	else
	{
		_bAdd = true;
		_typeToAdd = GT_SPHERE;
	}
}

//toggle the add primitive mode for box
void GRTViewport::toggleAddModeBox()
{
	if(_bAdd)
	{
		_bAdd = false;
		_typeToAdd = GT_NONE;
	}
	else
	{
		_bAdd = true;
		_typeToAdd = GT_BOX;
	}
}

//toggle the add primitive mode for cylinder
void GRTViewport::toggleAddModeCylinder()
{
	if(_bAdd)
	{
		_bAdd = false;
		_typeToAdd = GT_NONE;
	}
	else
	{
		_bAdd = true;
		_typeToAdd = GT_CYLINDER;
	}
}

//toggle the add primitive mode for cone
void GRTViewport::toggleAddModeCone()
{
	if(_bAdd)
	{
		_bAdd = false;
		_typeToAdd = GT_NONE;
	}
	else
	{
		_bAdd = true;
		_typeToAdd = GT_CONE;
	}
}

//toggle the add primitive mode for torus
void GRTViewport::toggleAddModeTorus()
{
	if(_bAdd)
	{
		_bAdd = false;
		_typeToAdd = GT_NONE;
	}
	else
	{
		_bAdd = true;
		_typeToAdd = GT_TORUS;
	}
}

//toggle the add primitive mode for cube mesh
void GRTViewport::toggleAddModeCubeMesh()
{
	if(_bAdd)
	{
		_bAdd = false;
		_typeToAdd = GT_NONE;
		_typeOfMeshToAdd = MESH_NONE;
	}
	else
	{
		_bAdd = true;
		_typeToAdd = GT_MESH;
		_typeOfMeshToAdd = MESH_CUBE;
	}
}

//toggle the add primitive mode for plan mesh
void GRTViewport::toggleAddModePlanMesh()
{
	if(_bAdd)
	{
		_bAdd = false;
		_typeToAdd = GT_NONE;
		_typeOfMeshToAdd = MESH_NONE;
	}
	else
	{
		_bAdd = true;
		_typeToAdd = GT_MESH;
		_typeOfMeshToAdd = MESH_PLAN;
	}
}

//deactivate the add mode
void GRTViewport::unsetAddMode()
{
	_bAdd = false;
	_typeToAdd = GT_NONE;
	_lightToAdd = LT_NONE;
}

//toggel the add light mode for directional
void GRTViewport::toggleAddModeGRTDirectionalLight()
{
	if(_bAdd)
	{
		_bAdd = false;
		_lightToAdd = LT_NONE;
	}
	else
	{
		_bAdd = true;
		_lightToAdd = LT_DIRECTIONAL;
	}
}

//toggle the add light mode for point light
void GRTViewport::toggleAddModeGRTPointLight()
{
	if(_bAdd)
	{
		_bAdd = false;
		_lightToAdd = LT_NONE;
	}
	else
	{
		_bAdd = true;
		_lightToAdd = LT_POINT;
	}
}

//toggle the add light mode for spot light
void GRTViewport::toggleAddModeGRTSpotLight()
{
	if(_bAdd)
	{
		_bAdd = false;
		_lightToAdd = LT_NONE;
	}
	else
	{
		_bAdd = true;
		_lightToAdd = LT_SPOT;
	}
}

//refresh the camera of the scene if the viewport is in camera scene mode
void GRTViewport::refreshCameraGRTScene()
{
	if(!_cameraGRTScene)
		return;

	_eye = SCENE.getCamera()->getTranslate();
	_at = SCENE.getCamera()->getAt();
	_up = SCENE.getCamera()->getUp();
}

//apply wireframe mode
void GRTViewport::applyWireframe()
{
	if (_wireframe)
		glPolygonMode( GL_FRONT_AND_BACK, GL_LINE );
	else
		glPolygonMode( GL_FRONT_AND_BACK, GL_FILL );
}

void GRTViewport::screenToWorld(int sx, int sy, float& wx, float& wy, float& wz)const
{
	//GLint viewport[4];
	//GLdouble modelview[16];
	//GLdouble projection[16];
	GLfloat winX, winY, winZ;
	GLdouble glX, glY, glZ;

	//glGetDoublev( GL_MODELVIEW_MATRIX, modelview );
	//glGetDoublev( GL_PROJECTION_MATRIX, projection );
	//glGetIntegerv( GL_VIEWPORT, viewport );

	winX = (float)sx;
	winY = (float)_viewport[3] - (float)sy;
	glReadPixels( sx, int(winY), 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &winZ );
	
	gluUnProject( winX, winY, winZ, _modelview, _projection, _viewport, &glX, &glY, &glZ);
	
	wx = glX;
	wy = glY;
	wz = glZ;
}

//rotate the camera
void GRTViewport::rotateCamera(int x, int y)
{
	//calculating delta
	RTVector3f delta = _mousePreviousPosition - RTVector3f(x, y, 0);

	//setting new previous position
	_mousePreviousPosition.x = x;
	_mousePreviousPosition.y = y;

	//decreasing delta (calculation comes from Qt documentation)
	const float sensitivity = 100;
	delta = delta * (1/(sensitivity * 3.14));

	//calculate camera angle
	_orientationCamera = _orientationCamera + RTVector3f(delta.y, delta.x, 0);

	//calculate rotation around x axis
	RTMatrix44 rotationX;
	rotationX.createRotationX(_orientationCamera.x);

	//calculate rotation around y axis
	RTMatrix44 rotationY;
	rotationY.createRotationY(_orientationCamera.y);

	//calculate translation
	RTMatrix44 translation;
	translation.createTranslation(_eye);

	//calculate the global transform matrix
	RTMatrix44 transform;
	transform = rotationX * rotationY * translation;

	//applying rotation
	_at = RTVector3f(0, 0, -1) * transform;
}

void GRTViewport::translateSelectedObject(int x, int y)
{
	//calculating delta
	float deltaX = x - _mousePreviousPosition.x;
	deltaX = deltaX * 0.03f;

	//calculate the transform matrix
	RTMatrix44 T;
	T.createTranslation(SCENE.getSelectedObject()->getTranslate());
	/*RTVector3f rotation = SCENE.getSelectedObject()->getRotate();
	rotation = rotation * 3.14f;
	rotation = rotation * (1.f/180.f);

	RTMatrix44 rX;
	rX.createRotationX(rotation.x);
	RTMatrix44 rY;
	rY.createRotationY(rotation.y);
	RTMatrix44 rZ;
	rZ.createRotationZ(rotation.z);*/

	RTMatrix44 R = SCENE.getSelectedObject()->getRotationMatrix();

	RTMatrix44 S;
	S.createScale(SCENE.getSelectedObject()->getScale());
	//RTMatrix44 transform = S * rZ * rY * rX * T;
	RTMatrix44 transform = S * R * T;

	//get the local vector
	RTVector3f h = SCENE.getSelectedObject()->_hoover;
	RTVector3f s = RTVector3f(0, 0, 0) * transform;
	RTVector3f e = SCENE.getSelectedObject()->_hoover * transform;
	RTVector3f dir = e - s;
	dir.normalize();

	//calculating translation vector
	RTVector3f translation = dir * deltaX;

	//applying translation
	SCENE.getSelectedObject()->translate(translation);
	
	//send the signals
	emit objectMoved(SCENE.getSelectedObject()->getTranslate().x, 
		SCENE.getSelectedObject()->getTranslate().y, SCENE.getSelectedObject()->getTranslate().z);

	//setting new previous position
	_mousePreviousPosition.x = x;
	_mousePreviousPosition.y = y;

	//send camera moved signal if camera moved
	if(SCENE.getSelectedObject()->getGRTMovableType() == MT_CAMERA)
		emit cameraGRTSceneMoved();
}

//rotate the selected object
void GRTViewport::rotateSelectedObject(int x, int y)
{
	//calculating delta
	float deltaY = y - _mousePreviousPosition.y;
	deltaY = deltaY * 0.3f;

	//calculating rotation vector
	RTVector3f rotation = SCENE.getSelectedObject()->_hoover * deltaY;

	//applying rotation
	SCENE.getSelectedObject()->rotate(rotation);

	//setting new previous position
	_mousePreviousPosition.x = x;
	_mousePreviousPosition.y = y;

	//send the rotated signal
	emit objectRotated(SCENE.getSelectedObject()->getRotate().x, SCENE.getSelectedObject()->getRotate().y,
		SCENE.getSelectedObject()->getRotate().z);

	//send camera moved signal if camera moved
	if(SCENE.getSelectedObject()->getGRTMovableType() == MT_CAMERA)
		emit cameraGRTSceneMoved();
}

//scale the selected object
void GRTViewport::scaleSelectedObject(int x, int y)
{
	//calculating delta
	float deltaX = x - _mousePreviousPosition.x;
	deltaX = deltaX * 0.03f;

	//calculate the transform matrix
	RTMatrix44 T;
	T.createTranslation(SCENE.getSelectedObject()->getTranslate());
	RTVector3f rotation = SCENE.getSelectedObject()->getRotate();
	rotation = rotation * 3.14f;
	rotation = rotation * (1.f/180.f);

	RTMatrix44 rX;
	rX.createRotationX(rotation.x);
	RTMatrix44 rY;
	rY.createRotationY(rotation.y);
	RTMatrix44 rZ;
	rZ.createRotationZ(rotation.z);
	RTMatrix44 S;
	S.createScale(SCENE.getSelectedObject()->getScale());
	RTMatrix44 transform = S * rZ * rY * rX * T;

	//get the local vector
	//RTVector3f h = SCENE.getSelectedObject()->_hoover;
	//RTVector3f s = RTVector3f(0, 0, 0) * transform;
	//RTVector3f e = SCENE.getSelectedObject()->_hoover * transform;
	//RTVector3f dir = e - s;
	//dir.normalize();

	//calculating scale vector
	//RTVector3f scale = dir * deltaX;
	RTVector3f scale = SCENE.getSelectedObject()->_hoover * deltaX;

	//applying scale
	SCENE.getSelectedObject()->scale(scale);
	
	//send the signals
	emit objectScaled(SCENE.getSelectedObject()->getScale().x, 
		SCENE.getSelectedObject()->getScale().y, SCENE.getSelectedObject()->getScale().z);

	//setting new previous position
	_mousePreviousPosition.x = x;
	_mousePreviousPosition.y = y;

}

//add a primitive to the scene
void GRTViewport::addPrimitive()
{
	//if no primitive type, we add nothing
	if(_typeToAdd == GT_NONE)
		return;

	//calculate the position of the object to add
	RTVector3f direction = _at - _eye;
	direction.normalize();
	RTVector3f translate = _eye + (direction * 10);

	//switch the type of sphere
	if(_typeToAdd == GT_SPHERE)
	{
		GRTGeoSphere* g = SCENE.createSphere();
		g->setTranslate(translate);
		SCENE.setSelectedObject(g);
		emit objectGRTGeometrySelected(g);
		emit objectGRTMovableSelected(g);
	}
	else if(_typeToAdd == GT_BOX)
	{
		GRTGeoBox* b = SCENE.createBox();
		b->setTranslate(translate);
		SCENE.setSelectedObject(b);
		emit objectGRTGeometrySelected(b);
		emit objectGRTMovableSelected(b);
	}
	else if(_typeToAdd == GT_CYLINDER)
	{
		GRTGeoCylinder* c = SCENE.createCylinder();
		c->setTranslate(translate);
		SCENE.setSelectedObject(c);
		emit objectGRTGeometrySelected(c);
		emit objectGRTMovableSelected(c);
	}
	else if(_typeToAdd == GT_CONE)
	{
		GRTGeoCone* c = SCENE.createCone();
		c->setTranslate(translate);
		SCENE.setSelectedObject(c);
		emit objectGRTGeometrySelected(c);
		emit objectGRTMovableSelected(c);
	}
	else if(_typeToAdd == GT_TORUS)
	{
		GRTGeoTorus* t = SCENE.createTorus();
		t->setTranslate(translate);
		SCENE.setSelectedObject(t);
		emit objectGRTGeometrySelected(t);
		emit objectGRTMovableSelected(t);
	}
	else if(_typeToAdd == GT_MESH)
	{
		//make the widhet the current opengl context
		makeCurrent();

		GRTGeoMesh* newMesh = SCENE.createMesh();
		newMesh->setTranslate(translate);
		SCENE.setSelectedObject(newMesh);
		emit objectGRTGeometrySelected(newMesh);
		emit objectGRTMovableSelected(newMesh);

		switch(_typeOfMeshToAdd)
		{
			case MESH_CUBE:
				newMesh->createCube(5);
				break;
			case MESH_PLAN:
				newMesh->createPlan(5);
				break;
			default:
				newMesh->createCube(5);
		}
	}

	_typeOfMeshToAdd = MESH_NONE;
	_typeToAdd = GT_NONE;
	emit objectAdded();
}

//add a lighht to the scene
void GRTViewport::addLight()
{
	//if no primitive type, we add nothing
	if(_lightToAdd == LT_NONE)
		return;

	//calculate the position of the object to add
	RTVector3f direction = _at - _eye;
	direction.normalize();
	RTVector3f translate = _eye + (direction * 10);

	//switch the type of sphere
	if(_lightToAdd == LT_DIRECTIONAL)
	{
		GRTDirectionalLight* l = SCENE.createGRTDirectionalLight();
		l->setTranslate(translate);
		l->getAtElement()->setTranslate(translate + RTVector3f(2, -2, 2));
		SCENE.setSelectedObject(l);
		emit objectLightSelected(l);
		emit objectGRTMovableSelected(l);
	}
	else if (_lightToAdd == LT_POINT)
	{
		GRTPointLight* newLight = SCENE.createGRTPointLight();
		newLight->setTranslate(translate);
		SCENE.setSelectedObject(newLight);
		emit objectLightSelected(newLight);
		emit objectGRTMovableSelected(newLight);
	}
	else if(_lightToAdd == LT_SPOT)
	{
		GRTSpotLight* newLight = SCENE.createGRTSpotLight();
		newLight->setTranslate(translate);
		newLight->getAtElement()->setTranslate(translate + RTVector3f(2, -2, 2));
		SCENE.setSelectedObject(newLight);
		emit objectLightSelected(newLight);
		emit objectGRTMovableSelected(newLight);
	}

	_lightToAdd = LT_NONE;
	emit objectAdded();
}

//calculate mark of the camera
void GRTViewport::getCameraMark(RTVector3f& right, RTVector3f& up, RTVector3f& front)const
{
	//calculate front vectors
	front = _at - _eye;
	front.normalize();

	//calculate right vectors
	right = _up.cross(front);
	right.normalize();

	up = _up;
	up.normalize();
}